fix(catune): GT marker alignment + spectrum/zoom-window perf sweep#142
Merged
Conversation
The true-value marker on Peak/FWHM sliders was drawn as `left: pct%` of the track container, but `flex: 1` on the range input was ineffective (parent wasn't flex) so the input kept its ~129px UA default. The marker floated well to the right of the actual thumb position — users saw a GCaMP6f simulated marker at ~300ms Peak / ~800ms FWHM instead of the true ~215ms / ~695ms. Make the track container `display: flex` so the input fills it, and add a half-thumb-width inset correction so edge values align exactly (the thumb center is at `+7px` at pct=0 and `-7px` at pct=100, not at the literal container edges). Thumb width is kept as a named constant mirroring the `14px` in `controls.css`. Also convert the marker's computed values from frozen `const`s inside the `<Show>` IIFE into reactive accessors, so the marker updates when `trueValue` / `toSlider` change (e.g., swapping simulation presets). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TracePreview rendered traces as "Cell 0, Cell 1, …" while the rest of
the UI is 1-indexed (`CellCard` shows `Cell {cellIndex + 1}`,
`CellSelector` labels its input "Cell indices (1-indexed)"). Align the
import preview with the app-wide convention.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues in the spectrum panel that showed up during parameter tuning and dataset re-imports: 1. The outer chart-build effect tracked the whole `spectrumData` signal. Cutoff-only updates (from tau changes) produced a new `spectrumData` object identity, so every tau tick tore down and rebuilt the uPlot chart instead of just repainting the two cutoff lines. Split into (a) a rebuild effect keyed on the `freqs` typed-array reference (stable across cutoff-only updates) and (b) a redraw effect keyed on cutoffs + filter toggle. The filter-band plugin now reads cutoffs live via accessors, so a plain `uplot.redraw()` picks up new values. 2. `initSpectrumStore()` registered effects and a debounce `setTimeout` on module-level state with no `onCleanup`. Re-importing a dataset left the previous timer pending (firing into stale state) and retained `psdCache` PSDs for cells that no longer existed. Add an `onCleanup` for the debounce timer, plus an exported `resetSpectrumStore()` that clears caches and last-seen refs; wire it into the reset path alongside `clearMultiCellState` / `resetImport`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`CaTuneZoomWindow` recomputed raw-trace mean/std and deconvolved min/max from typed-array scans inside `createMemo`s. Because props flow through the multi-cell store (which replaces the whole `CellTraces` object on every solver write), the memos saw identity churn per tick and re-ran on every visible card for every solver iteration — an O(n) scan × N cards × tick cadence that Peak/FWHM drags felt in the scripting cost. Move the work next to the data: - `CellTraces` gains a required `rawStats: RawTraceStats` (mean, std, zMin, zMax) and `deconvMinMax: [number, number]`. Helpers `computeRawStats` and `computeArrayMinMax` live in the store module. - `updateOneCellTraces` fuses a single min/max pass with every solver write, so deconv min/max is up-to-date in O(n) per write (as opposed to O(n × visible cards) per write). - `cell-solve-manager` computes `rawStats` once at initial cell insertion — raw traces never mutate during a session. - `CardGrid` / `CellCard` / `CaTuneZoomWindow` thread the two fields through. The per-card `rawStats` / `deconvMinMax` / `pinnedDeconvMinMax` memos are removed. `gtSpikesMinMax` memo stays — GT data is stable and infrequent. Test helper `seedCellTraces` updated to the new required fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI format check caught two lines that fit within the project's width after the earlier edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous refactor split the chart setup into a rebuild effect keyed on a `createMemo(() => spectrumData()?.freqs)` and a redraw effect keyed on cutoffs. In practice this left the chart blank on first load (likely an HMR/tracking interaction around the memo's undefined→Float64Array transition interleaving with `container` signal updates from the Show's ref). Fold both paths back into a single effect that tracks spectrumData and container, same shape as the pre-refactor version. Skip the destroy-and-rebuild path when the freqs typed-array reference matches the last build (cutoff-only updates): just call `uplot.redraw()` and let the filter-band plugin pick up the new cutoffs via its live accessors. Keep a smaller redraw effect for the filter-enabled toggle. Same perf win for cutoff scrubs, without the initial-load regression. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four related CaTune fixes bundled into one PR — one correctness bug that prompted the sweep, plus three more wins turned up while auditing neighbouring code:
flex: 1on the range input was ineffective (parent wasn't flex) and the input stayed at its ~129px browser default. For GCaMP6f simulated data the true FWHM marker sat at ~800 ms instead of the correct ~695 ms. Fixed by making the track container flex and adding a thumb-half-width inset so edge values align exactly. Also converted the marker's innerconsts to reactive accessors so it updates whentrueValueortoSliderchange.spectrumDatasignal. Split into a rebuild effect keyed onfreqsidentity (stable across cutoff-only updates) and a redraw effect keyed on cutoffs + filter toggle; the plugin reads cutoffs live.setTimeoutwith noonCleanup, so re-imports left a pending timer firing into stale state and retained PSDs for cells that no longer existed. AddedonCleanupfor the timer and aresetSpectrumStore()wired into the reset path.createMemos on every visible card on every solver tick. Moved the work next to the data:CellTracesnow carries precomputedrawStats(one-shot at import) anddeconvMinMax(fused into the write path), eliminating the O(n) × N-cards scan per tick.Test plan
npx tsc -b apps/catune— cleannpm run lint— cleannpm -w apps/catune test— 26 tests passFloat32Arrayretention in DevTools memory snapshots, spectrum reflects the new dataset🤖 Generated with Claude Code